package es.ucm.fdi.xtremeaudio.modelo.parser;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import es.ucm.fdi.xtremeaudio.modelo.beans.Biblioteca;
import es.ucm.fdi.xtremeaudio.modelo.beans.Cancion;
import es.ucm.fdi.xtremeaudio.modelo.beans.Playlist;
import es.ucm.fdi.xtremeaudio.modelo.excepciones.ReaderException;

/**
 * 
 * @author
 * 
 */
public class ParserSAXBiblio extends DefaultHandler {

	/*--------------------------------------------------------------------------
	Tenemos un par de arrays para saber Dentro de que elemento estamos 
	en un momento dado
	---------------------------------------------------------------------------*/
	private String[] sListaElementos = { "cancion", "autor", "album", "titulo",
			"genero", "anyo", "pista", "duracion", "formato", "path", "playlist", "nombre", "ruta" };
	private int iTotalElementos = sListaElementos.length;
	private boolean[] bDentro = new boolean[iTotalElementos];

	/*--------------------------------------------------------------------------
	Constantes para identificar para elemento
	---------------------------------------------------------------------------*/
	private static final int C_CANCION = 0;	
	private static final int C_AUTOR = 1;
	private static final int C_ALBUM = 2;
	private static final int C_TITULO = 3;
	private static final int C_GENERO = 4;
	private static final int C_ANYO = 5;
	private static final int C_PISTA = 6;
	private static final int C_DURACION = 7;
	private static final int C_FORMATO = 8;
	private static final int C_PATH = 9;
	private static final int C_PLAYLIST = 10;
	private static final int C_NOMBRE = 11;
	private static final int C_RUTA = 12;
	
	/*--------------------------------------------------------------------------
	Lista de canciones resultado de un parseo
	---------------------------------------------------------------------------*/
	private ArrayList<Cancion> vCanciones;
	
	/*--------------------------------------------------------------------------
	Cancion actualmente leido en un proceso de parseo
	---------------------------------------------------------------------------*/
	private Cancion cCancionActual;
	
	/*--------------------------------------------------------------------------
	Lista de Playlists resultado de un parseo
	---------------------------------------------------------------------------*/
	private ArrayList<Playlist> vPlaylists;
	
	/*--------------------------------------------------------------------------
	Playlist actualmente leida en un proceso de parseo
	---------------------------------------------------------------------------*/
	private Playlist pPlaylistActual;
	
	/*--------------------------------------------------------------------------
	Buffer de caracteres de un elemento en un proceso de parseo
	---------------------------------------------------------------------------*/
	private StringBuffer sBuffer = null;

	public ParserSAXBiblio() {
		for (int i = 0; i < iTotalElementos; i++) {
			bDentro[i] = false;
		}
	}

	public Biblioteca vParseFile(String filename)
			throws ReaderException {
		vCanciones = new ArrayList<Cancion>();
		vPlaylists = new ArrayList<Playlist>();
		try {
			// Obtener una clase SAXParserFactory para obtener un SAXParser
			SAXParserFactory oSAXParserFactory = SAXParserFactory.newInstance();

			// Definimos las opciones de parseo: validar el xml
			oSAXParserFactory.setValidating(true);

			// Definimos las opciones de parseo: namespace no activado
			oSAXParserFactory.setNamespaceAware(false);

			// Obtener una clase SAXParser
			SAXParser oSAXParser = oSAXParserFactory.newSAXParser();

			// Empezar a parsear el archivo con esta clase para recoger los
			// eventos de parseo
			oSAXParser.parse(new File(filename), this);

		} catch (FactoryConfigurationError e) {
			throw new ReaderException(
					"ReaderException[FactoryConfigurationError]: "
							+ e.toString());
		} catch (ParserConfigurationException e) {
			throw new ReaderException(
					"ReaderException[ParserConfigurationException]: "
							+ e.toString());
		} catch (SAXException e) {
			throw new ReaderException("ReaderException[SAXException]: "
					+ e.toString());
		} catch (IOException e) {
			throw new ReaderException("ReaderException[IOException]: "
					+ e.toString());
		}
		Biblioteca biblioteca = new Biblioteca(vCanciones,vPlaylists,0,-1,"Biblioteca");
		return biblioteca;
	}

	public void warning(SAXParseException e) throws SAXException {
		System.out.println("Warning: ");
		printInfo(e);
	}

	public void error(SAXParseException e) throws SAXException {
		System.out.println("Error: ");
		printInfo(e);
	}

	public void fatalError(SAXParseException e) throws SAXException {
		System.out.println("Fatal error: ");
		printInfo(e);
	}

	private void printInfo(SAXParseException e) {
		System.out.println("\tPublic ID: " + e.getPublicId());
		System.out.println("\tSystem ID: " + e.getSystemId());
		System.out.println("\tN\u00bamero de linea: " + e.getLineNumber());
		System.out.println("\tN\u00bamero de columna: " + e.getColumnNumber());
		System.out.println("\tMensaje: " + e.getMessage() + "\n");
	}

	public void startElement(String namespaceURI, String localName,
			String qualifiedName, Attributes attributes) throws SAXException {
		System.out.println("Elemento:");
		System.out.println("\t->> NamespaceURI: " + namespaceURI);
		System.out.println("\t->> Nombre local: " + localName);
		System.out.println("\t->> Nombre cualificado: " + qualifiedName);
		System.out.println("\t->> Atributos: " + attributes.toString() + "\n");

		// Si localName es vacio entonces el nombre del
		// elemento esta en qualifiedName
		String sNombreElemento = localName;
		if ("".equals(sNombreElemento))
			sNombreElemento = qualifiedName;

		// averiguar Dentro de que elemento estamos
		for (int i = 0; i < iTotalElementos; i++) {
			if (sNombreElemento.compareToIgnoreCase(sListaElementos[i]) == 0) {
				bDentro[i] = true;
				break;
			}
		}
		
		// Si el elemento es una cancion o una lista,
		// parsear el atributo (id)
		String id;
		if (sNombreElemento.compareToIgnoreCase(sListaElementos[ParserSAXBiblio.C_CANCION]) == 0) {
			cCancionActual = new Cancion();
			id = parseaId(attributes); 
	        System.out.println("ID de canci\u00f3n: " + id + "\n");
	        cCancionActual.setId(Integer.valueOf(id));
		}
		
		// Si el elemento es una playlist, se crea
		if (sNombreElemento.compareToIgnoreCase(sListaElementos[ParserSAXBiblio.C_PLAYLIST]) == 0) {
			pPlaylistActual = new Playlist();
			id = parseaId(attributes);
			System.out.println("ID de playlist: " + id + "\n");
	        pPlaylistActual.setId(Integer.valueOf(id));			
		}
	}
	
	private String parseaId(Attributes attributes){
		String id = null;
		for (int i = 0; i < attributes.getLength(); i++) {
        	// Buscamos el id de cancion o lista
        	// en localname o en getQName
            if ((attributes.getLocalName(i).compareToIgnoreCase("id") == 0) 
            	|| (attributes.getQName(i).compareToIgnoreCase("id") == 0)) {
                    id = attributes.getValue(i);
                    break;
            }
        }
		return id;
	}

	public void characters(char buf[], int offset, int len) throws SAXException {
		String s = new String(buf, offset, len);
		if (sBuffer == null)
			sBuffer = new StringBuffer(len);
		sBuffer.append(s.trim());
		System.out.println("\tValor: " + sBuffer + "\n");
	}

	public void endElement(String uri, String localName, String qualifiedName)
			throws SAXException {
		System.out.println("Cierre Elemento:");
		System.out.println("\t->>URI: " + uri);
		System.out.println("\t->>Nombre local: " + localName);
		System.out.println("\t->>Nombre cualificado: " + qualifiedName + "\n");

		if ("".equals(localName))
			localName = qualifiedName;

		// asignar contenido
		// si es un AUTOR establecer el contenido en la cancion actual
		if (bDentro[ParserSAXBiblio.C_AUTOR])
			cCancionActual.setAutor(sBuffer.toString());

		// si es un ALBUM establecer el contenido en la cancion actual
		if (bDentro[ParserSAXBiblio.C_ALBUM])
			cCancionActual.setAlbum(sBuffer.toString());

		// si es un TITULO establecer el contenido en la cancion actual
		if (bDentro[ParserSAXBiblio.C_TITULO])
			cCancionActual.setTitulo(sBuffer.toString());

		// si es un GENERO establecer el contenido en la cancion actual
		if (bDentro[ParserSAXBiblio.C_GENERO])
			cCancionActual.setGenero(sBuffer.toString());
		
		// si es un ANYO establecer el contenido en la cancion actual
		if (bDentro[ParserSAXBiblio.C_ANYO])
			cCancionActual.setAnyo(sBuffer.toString());
				
		// si es una PISTA establecer el contenido en la cancion actual
		if (bDentro[ParserSAXBiblio.C_PISTA])
			cCancionActual.setPista(sBuffer.toString());

		// si es una DURACION establecer el contenido en la cancion actual
		if (bDentro[ParserSAXBiblio.C_DURACION]){			
			String[] dur = sBuffer.toString().split(":");			
			int d = (Integer.valueOf(dur[0]) * 60) + Integer.valueOf(dur[1]);
			cCancionActual.setDuracion(d);
			cCancionActual.setStringDuracion(sBuffer.toString());
		}

		// si es un FORMATO establecer el contenido en la cancion actual
		if (bDentro[ParserSAXBiblio.C_FORMATO])
			cCancionActual.setFormato(sBuffer.toString());		

		// si es un PATH establecer el contenido en la cancion actual
		if (bDentro[ParserSAXBiblio.C_PATH])
			cCancionActual.setPath(sBuffer.toString());
		
		// si es un NOMBRE establecer el contenido en la playlist actual
		if (bDentro[ParserSAXBiblio.C_NOMBRE])
			pPlaylistActual.setNombre(sBuffer.toString());
		
		// si es una RUTA establecer el contenido en la playlist actual
		if (bDentro[ParserSAXBiblio.C_RUTA])
			pPlaylistActual.setRuta(sBuffer.toString());
		
		if (localName.compareToIgnoreCase(sListaElementos[ParserSAXBiblio.C_CANCION]) == 0) {
			
			// si es una cancion la incluimos en la lista de canciones
			vCanciones.add(cCancionActual);
		}
		
		if (localName.compareToIgnoreCase(sListaElementos[ParserSAXBiblio.C_PLAYLIST]) == 0) {
		
			// si es una cancion la incluimos en la lista de playlists
			vPlaylists.add(pPlaylistActual);
		}

		// Limpiamos sBuffer
		sBuffer = null;
				
		/*------------------------------------------------------------------------
		Actualizar array bDentro
		-------------------------------------------------------------------------*/
		for (int i = 0; i < iTotalElementos; i++) {
			if (localName.compareToIgnoreCase(sListaElementos[i]) == 0) {
				bDentro[i] = false;
				break;
			}
		}
	}
}
